<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>从化绿波-静态绿波</title>
</head>
<link href="../../css/green-ware.css" rel="stylesheet">
<link href="../../css/index.css" rel="stylesheet">

<body>
  <div class="page-container" id="green-ware">
    <div class="page-select">
      <div v-for="item in pageTab" @click="changeTab(item)" :key="item.key" class="select-item"
        :class="item.key === 'static-green' ? 'active-select': ''">
        {{item.label}}
      </div>
      <img class="login-out" src="../../assets/exit.png" alt="退出登录" title="退出登录" @click="handleLoginOut" />
    </div>
    <div class="iframe-container">
      <div class="green-ware page-warp">
        <div class="message" :class="message.type" v-if="message.text">{{message.text}}</div>
        <template v-if="alert.text">
          <div class="dialog-cover alert-dialog-cover"></div>
          <div class="alert-dialog dialog">
            <div class="dialog-title alert-dialog-title">
              提示
            </div>
            <div class="dialog-content">
              {{alert.text}}
            </div>
            <div class="dialog-footer alert-dialog-footer">
              <template v-if="alert.type === MESSAGE_TYPE.ALERT">
                <button class="primary-btn" @click="cancelConfirm">确定</button>
              </template>
              <template v-if="alert.type === MESSAGE_TYPE.CONFIRM">
                <button class="cancel-btn" @click="cancelConfirm">取消</button>
                <button class="primary-btn" @click="handleConfirm(alert)">确定</button>
              </template>
            </div>
          </div>
        </template>
        <!-- 搜索栏目 -->
        <div class="search-form">
          <div class="search-item">
            <div class="search-label">所属子区:</div>
            <select v-model="searchObj.ssId" style="width: 100%;text-align: center;">
              <option v-for="item in SUB_LIST" :key="item.master" :value="item.master">{{item.master}}</option>
            </select>
          </div>
          <div class="search-item">
            <div class="search-label">方案:</div>
            <select v-model="searchObj.plno" style="width: 100%;text-align: center;">
              <option v-for="item in SCHEME_CONFIG" :key="item" :value="item">{{item}}</option>
            </select>
          </div>
          <div class="search-item">
            <div class="search-label">速度(km/h):</div>
            <input v-model="searchObj.speed"
              onkeydown="return event.keyCode !== 69 && event.keyCode !== 189" 
              oninput="this.value = Math.abs(this.value)"
              type="number"
            />
          </div>
          <div class="search-item flex-2">
            <!-- <button @click="handleReset" class="cancel-btn">重置</button> -->
            <button @click="handleSearch" class="primary-btn">
              <img src="../../assets/jiazai.png" class="loading" v-if="loading.search" />
              查询
            </button>
          </div>
        </div>
        <!-- 图例 -->
        <div class="legend-content">
          <div class="legend-item">图例:</div>
          <div v-for="item in legend" :key="item.color" class="legend-item">
            <div class="legend-chart"
              :style="item.showBorder ? `border-top: 2px dashed ${item.color}; border-bottom: 2px dashed ${item.color}` : `background-color: ${item.color}`">
            </div>
            <div>{{item.text}}</div>
          </div>
        </div>
        <div class="green-ware-chart" :id="`TimePort_${randomId}`">
          <svg v-if="!isResize && !isEmpty" class="time-port-svg" id="TimePortSvg" xmlns="http://www.w3.org/2000/svg"
            version="1.1">
            <!-- 正反向 -->
            <g v-for="typeItem in timePort" :key="typeItem.type">
              <g v-if="greenData[typeItem.type] && greenData[typeItem.type].length">
                <g v-for="(child, childIndex) in greenData[typeItem.type]" :key="childIndex + typeItem.type">
                  <!-- 绘制竖着的几条线 -->
                  <line :x1="getVerticalLinePosition(childIndex, typeItem.type)" y1="0"
                    :x2="getVerticalLinePosition(childIndex, typeItem.type)" :y2="greenSvgHeight()"
                    :style="`stroke:${typeItem.color};stroke-width:2`" />
                  <!-- 绘制竖线上的几个段 -->
                  <g v-for="(item, index) in pointNumber " :key="'greenStartTime' + index">
                    <line :x1="getTimeItemXPosition(childIndex, typeItem.type)"
                      :y1="getTimeItemYPosition(child, index).y1" :x2="getTimeItemXPosition(childIndex, typeItem.type)"
                      :y2="getTimeItemYPosition(child, index).y2"
                      :style="`stroke: ${typeItem.stroke};stroke-width: 6`" />
                  </g>
                  <!-- 绘制绿波带 -->
                  <g v-for="(item, index) in pointNumber" :key="'greenWaveBandWidths' + index">
                    <polygon :id="`greenWave_${typeItem.type}_${childIndex}_${index}`"
                      :points="getLinePosition(index, childIndex, typeItem.type)?.base"
                      :style="`fill:${typeItem.fill}`" />
                    <text v-if="getTimeTextPosition(index, childIndex, typeItem.type) && child.bandwidth"
                      class="interval-time" :x="getTimeTextPosition(index, childIndex, typeItem.type)?.x"
                      :y="getTimeTextPosition(index, childIndex, typeItem.type)?.y">
                      {{ typeItem.type === TIME_PORT_TYPE.FORWARD ? child.bandwidth : greenData[typeItem.type]?.[childIndex + 1]?.bandwidth }}s
                    </text>
                  </g>
                  <!-- 绘制理想绿波带 -->
                  <g v-if="childIndex === 0">
                    <line :x1="getOptimizeLinePosition(typeItem.type, childIndex).x1"
                      :y1="getOptimizeLinePosition(typeItem.type, childIndex).y1"
                      :x2="getOptimizeLinePosition(typeItem.type, childIndex).x3"
                      :y2="getOptimizeLinePosition(typeItem.type, childIndex).y4"
                      :style="`stroke: red ;stroke-width: 2`" stroke-dasharray="5,5" />
                    <line :x1="getOptimizeLinePosition(typeItem.type, childIndex).x1"
                      :y1="getOptimizeLinePosition(typeItem.type, childIndex).y2"
                      :x2="getOptimizeLinePosition(typeItem.type, childIndex).x3"
                      :y2="getOptimizeLinePosition(typeItem.type, childIndex).y3" :style="`stroke: red;stroke-width: 2`"
                      stroke-dasharray="5,5" />
                  </g>
                  <!-- 相位差 -->
                  <g v-if="child.phaseDiffTimes">
                    <line :x1="getGreenStartPosition(childIndex, typeItem.type).x"
                      :y1="getGreenStartPosition(childIndex, typeItem.type).y"
                      :x2="getGreenStartPosition(childIndex, typeItem.type).x" :y2="greenSvgHeight()"
                      :style="`stroke: rgb(0,0,0);stroke-width: 1`" />
                    <line :x1="getGreenStartPosition(childIndex, typeItem.type).x - 4"
                      :y1="getGreenStartPosition(childIndex, typeItem.type).y"
                      :x2="getGreenStartPosition(childIndex, typeItem.type).x + 4"
                      :y2="getGreenStartPosition(childIndex, typeItem.type).y"
                      :style="`stroke: rgb(0,0,0);stroke-width: 1`" />
                    <line :x1="getGreenStartPosition(childIndex, typeItem.type).x - 4" :y1="greenSvgHeight()"
                      :x2="getGreenStartPosition(childIndex, typeItem.type).x + 4" :y2="greenSvgHeight()"
                      :style="`stroke: rgb(0,0,0);stroke-width: 1`" />
                    <text class="interval-time green-time" :x="getGreenStartTextPosition(childIndex, typeItem.type).x"
                      :y="getGreenStartTextPosition(childIndex, typeItem.type).y">
                      {{ child.phaseDiffTimes }}
                    </text>
                  </g>
                </g>
              </g>
            </g>
            <!-- Y轴 -->
            <g>
              <line :x1="safeSpace - 20" :x2="safeSpace - 20" y1="0" :y2="xSvgHeight()"
                :style="`stroke: rgb(0,0,0);stroke-width: 1`" />
              <g v-for="item in chartY()" :key="item">
                <!-- Y轴分割线 -->
                <line v-if="item % 100 === 0" :x1="safeSpace - 25" :x2="safeSpace - 20"
                  :y1="greenSvgHeight() - heightRate() * item" :y2="greenSvgHeight() - heightRate() * item"
                  :style="`stroke: rgba(0,0,0, 1);stroke-width: 1`" />
                <line :x1="safeSpace - 20" x2="100%" :y1="greenSvgHeight() - heightRate() * item"
                  :y2="greenSvgHeight() - heightRate() * item" :style="`stroke: rgba(0,0,0, 0.1);stroke-width: 1`" />
                <!-- Y轴文字 -->
                <text :key="item" v-if="item % 100 === 0" class="interval-time chart-y-item" text-anchor="right"
                  :x="item < 100 ? 6 : 0" :y="greenSvgHeight() - heightRate() * item + 4">
                  {{ item }}
                </text>
              </g>
              <!-- Y轴单位 -->
              <text class="interval-time chart-y-item" x="2.2%" :y="15">时间(s)</text>
            </g>
            <!-- X轴 -->
            <g>
              <!-- X轴的基线 -->
              <line :x1="safeSpace - 20" x2="100%" :y1="xSvgHeight()" :y2="xSvgHeight()"
                :style="`stroke: rgb(0,0,0);stroke-width: 1`" />
              <!-- X轴单位 -->
              <text class="interval-time chart-y-item" x="96%" :y="xSvgHeight() - 5">距离(m)</text>
              <!-- X轴的距离 --- 正向 -->
              <g v-if="greenData?.forward?.length">
                <g v-for="(item, index) in sectionLens" :key="index + 'sectionLens'">
                  <!-- 距离左边的分割线 -->
                  <line :x1="xLinePosition(index, TIME_PORT_TYPE.FORWARD) + 5"
                    :x2="xLinePosition(index, TIME_PORT_TYPE.FORWARD) + 5" :y1="greenSvgHeight()"
                    :y2="greenSvgHeight() + 10" :style="`stroke: rgba(0,0,0, 0.7);stroke-width: 1`" />
                  <!-- 距离的数值 -->
                  <text :x="xTextPosition(index)" :y="greenSvgHeight() + 20" text-anchor="middle" font-size="12">
                    {{ item }}m
                  </text>
                  <!-- 距离右边的分割线 -->
                  <line :x1="xLinePosition(index + 1, TIME_PORT_TYPE.FORWARD) + 5"
                    :x2="xLinePosition(index + 1, TIME_PORT_TYPE.FORWARD) + 5" :y1="greenSvgHeight()"
                    :y2="greenSvgHeight() + 10" :style="`stroke: rgba(0,0,0, 0.7);stroke-width: 1`" />
                  <!-- 距离的长度表示线 -->
                  <line :x1="xLinePosition(index, TIME_PORT_TYPE.FORWARD) + 5"
                    :x2="xLinePosition(index + 1, TIME_PORT_TYPE.FORWARD) + 5" :y1="greenSvgHeight() + 5"
                    :y2="greenSvgHeight() + 5" :style="`stroke: rgba(0,0,0, 0.7);stroke-width: 1`" />
                </g>
              </g>
              <!-- X轴上的路段名称 -->
              <g v-for="(item, index) in crossInfo" :key="item.crossId">
                <text text-anchor="middle" font-size="12" :x="xLinePosition(index)" :y="greenSvgHeight() + 40">{{
                  item.crossName }}</text>
              </g>
            </g>
          </svg>
          <div class="no-data" v-if="isEmpty">
            暂无时距图数据
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
<script src="../../js/vue3.js"></script>
<script src="../../js/mock/firstEdition/staticMock.js"></script>
<script src="../../config.js"></script>
<script src="../../js/api/secondEdition.js"></script>
<script src="../../js/dict/greenConfig.js"></script>
<script src="../../js/dict/schemeConfig.js"></script>
<script src="../../js/method.js"></script>

<script>
  const { createApp, ref, nextTick, reactive, onMounted, onUnmounted, watch, computed } = Vue
  const app = createApp({
    setup() {
      // -------------- 时距图计算相关逻辑 start --------------
      // 基础信息的高度
      const baseMsgHeight = 50

      // Y轴的时间
      const baseY = computed(() => {
        return allLength.value / (70 * 0.278) * 2
      })

      // 循环的方案次数
      const pointNumber = 20

      const TIME_PORT_TYPE = {
        FORWARD: 'forward',
        REVERSE: 'reverse'
      }

      /**
     * 路线距离信息 --- 绘制时候基线（必定是正向的距离）
     */
      const sectionLens = ref([])
      const reverseLens = ref([])
      // 路口信息
      const crossInfo = ref([])
      // 干线绿波数据
      const greenData = reactive({
        forward: [],
        reverse: []
      })

      /**
      * 所有路段的长度，用于计算竖线的位置
      */
      const allLength = computed(() => {
        const total = sectionLens.value.reduce((acc, curr) => acc + curr, 0)
        return total + total * 0.015
      })

      /**
     * 获取基线在X轴的位置 转换为px
     * @param currentIndex 当前基线的索引值
     */
      const getTimeLinePosition = (currentIndex) => {
        // 获取当前线段所在的位置
        let currentLength = 0
        sectionLens.value.forEach((element, index) => {
          if (index < currentIndex) {
            currentLength += element
          }
        })
        if (currentLength > 0) {
          currentLength = currentLength / allLength.value * getSvgWidth.value
        }
        return currentLength + safeSpace
      }


      /**
    * 根据类型获取竖线在X轴的位置
    * @param index 当前竖线的索引值
    * @param type 正反向
    */
      const getVerticalLinePosition = (index, type) => {
        let x = getTimeLinePosition(index)
        // 错开正向和反向的数据，将反向的数据全部往左边偏移0.5%
        if (type === TIME_PORT_TYPE.REVERSE) {
          x = x + 10
        }
        return x
      }


      /**
   * X轴上线条的位置
   * @param index
   * @param type
   * @returns
   */
      const xLinePosition = (index, type = TIME_PORT_TYPE.FORWARD) => {
        const x = getVerticalLinePosition(index, type)
        return x
      }

      /**
       * X轴上线条的位置
       * @param index
       * @param type
       * @returns
       */
      const xTextPosition = (index) => {
        const x1 = xLinePosition(index)
        const x2 = xLinePosition(index + 1)
        const x = (x1 + x2) / 2
        return x
      }

      /**
        * 获取每个段的X轴位置
        * @param index 当前竖线的索引值
        * @param type 正反向
        */
      const getTimeItemXPosition = (index, type) => {
        let x = getTimeLinePosition(index)
        // 错开正向和反向的数据，将反向的数据全部往左边偏移0.45%
        // 错开正向和反向的数据，将正向的数据全部往右边偏移0.005%
        if (type === TIME_PORT_TYPE.REVERSE) {
          x = x + 10
        }
        return x
      }

      /**
         * 计算柱子1的对应比例
         */
      const heightRate = () => {
        let rate = 1
        if (baseY.value) {
          const heightRate = greenSvgHeight() / baseY.value
          rate = heightRate > 1 ? 1 : heightRate
        }
        return rate
      }

      /**
         * 获取每个段的Y轴位置,Y轴的距离与实际距离是反过来的
         * 实际应用：标注每个路口的绿灯时长
         * @param item 当前的数据
         * @param index 当前的索引值
         */
      const getTimeItemYPosition = (item, index) => {
        // y1 = 当前索引值*周期长度(schemeCycle) + 方案周期开始到协调相位绿灯开始前的时长(greenPreTime)+ 相位差时间(phaseDiffTimes)
        const y1 = heightRate() * Number(item.schemeCycle * index + item.greenPreTime + item.phaseDiffTimes)
        const duration = heightRate() * Number(item.greenStartTime)
        // y1 = y1 + 协调相位绿灯时长(greenStartTime)
        const y2 = y1 + duration
        return {
          y1: greenSvgHeight() - y1,
          y2: greenSvgHeight() - y2
        }
      }

      /**
        * 获取svg父元素的宽度
        */
      const getSvgWidth = computed(() => {
        const currentLine = document.getElementById(`TimePort_${randomId.value}`)
        const width = currentLine?.getBoundingClientRect().width
        const resultWidth = width - safeSpace
        return resultWidth
      })


      /**
        * 获取绿波线的四个位置
        * @param currentIndex 当前的方案周期索引值
        * @param index 当前路口索引值类型
        * @param type 正反向
        */
      const getLinePosition = computed(() => {
        return (currentIndex, crossIndex, type) => {
          // 获取y数据的index值，因为正向的最后一项无数据，反向的第一项无数据
          const currentCrossIndex = type === TIME_PORT_TYPE.FORWARD ? crossIndex : crossIndex + 1
          const nextCrossIndex = type === TIME_PORT_TYPE.FORWARD ? crossIndex + 1 : crossIndex
          // 如果图表没加载完成，不往下执行
          const currentData = greenData[type][currentCrossIndex]
          const nextData = greenData[type][nextCrossIndex]
          // if (type === TIME_PORT_TYPE.REVERSE) {
          //   console.log('currentCrossIndex', currentCrossIndex, currentData);
          //   console.log('nextCrossIndex', nextCrossIndex, nextData);
          // }
          const currentSection = sectionLens.value[type === TIME_PORT_TYPE.FORWARD ? currentCrossIndex : nextCrossIndex]

          if (!nextData || !currentData) { return }
          const svgWidth = getSvgWidth.value

          if (!svgWidth) { return }
          const x1 = getTimeItemXPosition(currentCrossIndex, type)
          const x2 = x1
          const x3 = getTimeItemXPosition(nextCrossIndex, type)
          const x4 = x3
          // y1, y2 = 当前索引值*周期长度(schemeCycle) + 当前方案周期开始到协调相位绿灯开始前的时长(greenPreTime)+ 当前相位差时间(phaseDiffTimes)
          let y1 = currentData.schemeCycle * currentIndex + currentData.phaseDiffTimes + currentData.greenPreTime
          let y2 = y1 + currentData.bandwidth
          // y3 y3 = y1 + 当前路段距离 / 当前速度
          let y4 = y1 + currentSection / (currentData.speed * 0.278)
          let y3 = y4 + currentData.bandwidth
          y1 = greenSvgHeight() - heightRate() * Number(y1)
          y2 = greenSvgHeight() - heightRate() * Number(y2)
          y3 = greenSvgHeight() - heightRate() * Number(y3)
          y4 = greenSvgHeight() - heightRate() * Number(y4)

          if (y3 > greenSvgHeight() || y4 > greenSvgHeight()) { return }
          return {
            base: `${x1} ${y1}, ${x2} ${y2}, ${x3} ${y3}, ${x4} ${y4}`,
            x1,
            x2,
            x3,
            x4,
            y1,
            y2,
            y3,
            y4
          }
        }
      })


      // 获取理想绿波带的位置
      // 规则：
      // 1. 从第一个路口开始，到最后一个路口结束
      const getOptimizeLinePosition = (type, crossIndex) => {
        const currentCrossIndex = type === TIME_PORT_TYPE.FORWARD ? crossIndex : crossIndex + 1
        const nextCrossIndex = type === TIME_PORT_TYPE.FORWARD ? crossIndex + 1 : crossIndex
        const startIndex = type === TIME_PORT_TYPE.FORWARD ? 0 : sectionLens.value.length
        const endIndex = type === TIME_PORT_TYPE.FORWARD ? sectionLens.value.length : 0
        const startData = greenData[type][startIndex]
        const svgWidth = getSvgWidth.value
        const currentSection = sectionLens.value[type === TIME_PORT_TYPE.FORWARD ? currentCrossIndex : nextCrossIndex]
        if (!svgWidth) { return }
        const x1 = getTimeItemXPosition(startIndex, type)
        const x2 = x1
        const x3 = getTimeItemXPosition(endIndex, type)
        const x4 = x3
        // y1, y2 = 当前方案周期开始到协调相位绿灯开始前的时长(greenPreTime)+ 当前相位差时间(phaseDiffTimes)
        let y1 = startData.phaseDiffTimes + startData.greenPreTime
        let y2 = y1 + startData.bandwidth
        // y3 y3 = y1 + 当前路段距离 / 当前速度
        let y4 = y1 + allLength.value / (startData.speed * 0.278)
        let y3 = y4 + startData.bandwidth
        y1 = greenSvgHeight() - heightRate() * Number(y1)
        y2 = greenSvgHeight() - heightRate() * Number(y2)
        y3 = greenSvgHeight() - heightRate() * Number(y3)
        y4 = greenSvgHeight() - heightRate() * Number(y4)
        return {
          base: `${x1} ${y1}, ${x2} ${y2}, ${x3} ${y3}, ${x4} ${y4}`,
          x1,
          x2,
          x3,
          x4,
          y1,
          y2,
          y3,
          y4
        }
      }


      /**
         * 获取文字的位置
         * @param item 当前的数据
         * @param index 当前的类型
         * @param type 正反向
         */
      const getTimeTextPosition = (item, index, type) => {
        const data = getLinePosition.value(item, index, type)
        if (!data) { return }
        const x = (data.x1 + data.x3) / 2
        const y = (data.y1 + data.y3) / 2
        return {
          y, x: TIME_PORT_TYPE.FORWARD === type ? x + 25 : x - 25
        }
      }

      /**
       * 获取绿间隔文字的位置
       * @param index
       * @param type
       */
      const getGreenStartTextPosition = (index, type) => {
        const data = greenData[type]
        const y = data && data[index] ? heightRate() * data[index].phaseDiffTimes / 2 : 0
        let x = getTimeItemXPosition(index, type)
        if (type === TIME_PORT_TYPE.REVERSE) {
          x = x - 35
        } else {
          x = x + 15
        }
        return {
          y: greenSvgHeight() - y,
          x: x
        }
      }


      /**
         * 获取绿间隔绘图的位置
         * @param index
         * @param type
         */
      const getGreenStartPosition = (index, type) => {
        const data = greenData[type]
        const y = data && data[index] ? heightRate() * data[index].phaseDiffTimes : 0
        let x = getTimeItemXPosition(index, type)
        if (type === TIME_PORT_TYPE.FORWARD) {
          x = x - 4
        } else {
          x = x + 4
        }
        return {
          y: greenSvgHeight() - y,
          x
        }
      }


      /**
        * 动态左侧坐标轴
        * 逻辑：
        * 1. 根据后端给的Y轴值，进行绘制坐标轴，绘制10个点
        * 2. 坐标轴最大值小于300时，图的展示不美观，所以会做最小值控制
        */
      const chartY = () => {
        const list = []
        if (!baseY.value) { return [] }
        const data = baseY.value < 300 ? 300 : baseY.value
        // const diffLength = data / 20
        for (let i = 0; i * 10 <= baseY.value; i++) {
          list.push(i * 10)
        }
        return list
      }

      // 图表的高度
      // 
      let chartHeight = 500

      // 绘图的安全距离
      const safeSpace = 50

      /**
       * 获取svg父元素的高度
       */
      const getSvgHeight = () => {
        return chartHeight - safeSpace
      }
      /**
     * y轴与像素(px)的转换
     * 1s代表几个像素
     */
      const yTransformPx = () => {
        const svgHeight = getSvgHeight()
        // const disTotal = getTimeTotal()
        return svgHeight / baseY.value
      }

      /**
       * x轴与像素(px)的转换
       * 1米代表几个像素
       */
      const xTransformPx = () => {
        const svgWidth = getSvgWidth.value
        const disTotal = getDisTotal()
        return svgWidth / disTotal
      }

      /**
    * 干线绿波绘图的区域高度
    * 预留一定的位置展示距离和路口信息
    */
      const greenSvgHeight = () => {
        return chartHeight - baseMsgHeight
      }

      /**
     * x轴的高度
     */
      const xSvgHeight = () => {
        return (chartHeight + greenSvgHeight()) / 2
      }
      // -------------- 时距图计算相关逻辑 end --------------


      // -------------- 界面渲染逻辑 start --------------

      const randomId = ref(Math.random())
      let isResize = ref(false)
      const pageResize = () => {
        randomId.value = Math.random()
        // 防抖，防止重复绘制或者多次触发绘制
        if (isResize.value) { return }
        isResize.value = true
        chartHeight = window.innerHeight - 150
        nextTick(() => {
          setTimeout(() => {
            isResize.value = false
          }, 1000)
        })
      }

      /**
       * 是否空数据
       */
      const isEmpty = computed(() => {
        return !greenData?.forward?.length && !greenData?.reverse?.length
      })

      const timePort = [
        { type: TIME_PORT_TYPE.REVERSE, color: 'rgba(255, 0, 255, 0.6)', fill: 'rgba(0, 204, 255, 0.8)', stroke: 'rgba(0, 204, 255, 1)' },
        { type: TIME_PORT_TYPE.FORWARD, color: 'rgba(255, 0, 0, 0.6)', fill: 'rgba(127, 191, 127, 0.8)', stroke: 'rgba(127, 191, 127, 1)' }
      ]


      // 图例
      const legend = [
        { color: 'rgba(0, 204, 255, 0.8)', text: '反向绿波' },
        { color: 'rgba(127, 191, 127, 0.8)', text: '正向绿波' },
        { color: 'rgb(255, 0, 0)', text: '理想状态绿波', showBorder: true },
      ]

      const MESSAGE_TYPE = {
        'SUCCESS': 'success',
        'INFO': 'info',
        'ALERT': 'alert',
        'ERROR': 'error',
        'CONFIRM': 'confirm'
      }

      const message = reactive({
        type: '',
        text: ''
      })

      const alert = reactive({
        type: '',
        text: '',
        methods: '',
        item: ''
      })

      // 弹窗消息1.5秒消失
      watch(
        () => message.text,
        () => {
          if (message.text) {
            setTimeout(() => {
              message.text = ''
            }, 2000)
          }
        }
      )

      // ------------- 搜索列表 ---------------
      const searchObj = reactive({
        ssId: '',
        plno: '',
        speed: 60
      })

      const loading = reactive({
        search: false
      })

      // 基础数据拼接
      const dataConcat = () => {
        greenData.forward = []
        greenData.reverse = []
        sectionLens.value = []
        crossInfo.value = []
        // 当前的方案信息
        let currentScheme = []
        allSchemeList.value.forEach(item => {
          if (currentSlaveList.value.includes(item.site) && item.plno === searchObj.plno) {
            currentScheme.push(item)
          }
        })
        if (currentScheme.length !== currentSlaveList.value.length) {
          message.type = MESSAGE_TYPE.ALERT
          message.text = '当前子区配置方案不完整，请检查方案信息'
          return
        }
        currentSlaveList.value.forEach((item, index) => {
          crossInfo.value.push({
            crossId: item,
            crossName: item
          })
          if (index > 0) {
            ROAD_LENGTH[item] && sectionLens.value.push(ROAD_LENGTH[item].forward)
          }
        })
        const { forward, reverse } = greenDataInit(currentScheme)
        const resultData = {
          forwardSectionLens: sectionLens.value,
          reverseSectionLens: sectionLens.value,
          crossInfo: crossInfo.value,
          forward,
          reverse
        }
        greenData.forward = forward
        greenData.reverse = reverse
      }

      // 单独处理绿波数据
      const greenDataInit = (schemeList) => {
        let forward = []
        let reverse = []
        currentSlaveList.value.forEach(site => {
          const item = schemeList.find(i => i.site === site)
          forward.push({
            crossId: item.site,
            bandwidth: getGreenTime(item, TIME_PORT_TYPE.FORWARD),
            phaseDiffTimes: item.offset,
            schemeId: null,
            schemeCycle: item.cycle,
            greenStartTime: getGreenTime(item, TIME_PORT_TYPE.FORWARD),
            greenPreTime: 0,
            speed: searchObj.speed
          })
          reverse.push({
            crossId: item.site,
            bandwidth: getGreenTime(item, TIME_PORT_TYPE.REVERSE),
            phaseDiffTimes: item.offset,
            schemeId: null,
            schemeCycle: item.cycle,
            greenStartTime: getGreenTime(item, TIME_PORT_TYPE.REVERSE),
            greenPreTime: 0,
            speed: searchObj.speed
          })
        })
        return {
          forward,
          reverse
        }
      }

      const getGreenTime = (item, type) => {
        let greenTime = 0
        // 获取当前方向的协调相位
        const coordinateConfig = ROAD_COORDINATE[item.site]?.[type]
        if (!coordinateConfig) {
          message.type = MESSAGE_TYPE.ALERT
          message.text = '当前路口没有配置协调相位，请检查'
          return 0
        }
        // 绿信比分隔
        const greenSplit = item.split.split(', ')
        // 绿信比配置
        const greenConfig = {}
        // 绿信比的Key值
        let greenKey = ''
        greenSplit.forEach(item => {
          const data = item.split(':')
          greenConfig[data[0]] = Number(data[1])
          greenKey += data[0]
        })

        greenTime = calculateSum(greenConfig, coordinateConfig)
        return greenTime
      }

      function calculateSum(data, key) {
        const originalKeys = Object.keys(data);
        const inputKeys = [...new Set([...key])]; // 去重

        // 1. 检查所有输入键是否都存在
        const existingKeys = inputKeys.filter(k => originalKeys.includes(k));
        if (existingKeys.length === 0) return 0;

        // 2. 在原始键顺序中查找包含这些键的最小区间
        let minIndex = originalKeys.length;
        let maxIndex = -1;

        for (const k of existingKeys) {
          const idx = originalKeys.indexOf(k);
          minIndex = Math.min(minIndex, idx);
          maxIndex = Math.max(maxIndex, idx);
        }

        // 3. 检查这个区间是否正好包含所有存在的键（不多不少）
        const contiguousKeys = originalKeys.slice(minIndex, maxIndex + 1);
        const isContiguous = existingKeys.every(k => contiguousKeys.includes(k)) &&
          contiguousKeys.every(k => existingKeys.includes(k) || !inputKeys.includes(k));

        if (isContiguous) {
          return existingKeys.reduce((sum, k) => sum + data[k], 0);
        }

        // 4. 如果没有完全连续，找最长连续子序列
        let maxSum = 0;
        let currentSum = 0;
        let currentCount = 0;

        for (let i = 0; i < originalKeys.length; i++) {
          const k = originalKeys[i];
          if (existingKeys.includes(k)) {
            currentSum += data[k];
            currentCount++;
            maxSum = Math.max(maxSum, currentSum);
          } else {
            currentSum = 0;
            currentCount = 0;
          }
        }

        // 5. 返回结果
        return maxSum > 0 ? maxSum : data[existingKeys[0]];
      }

      // 搜索
      const handleSearch = () => {
        let flag = false
        for (let i in searchObj) {
          if (!searchObj[i]) {
            flag = true
          }
        }
        if (flag) {
          message.type = MESSAGE_TYPE.ALERT
          message.text = '请完善搜索框内容'
          return
        }
        if (loading.search) return
        window.sessionStorage.setItem('GREEN_DATA', JSON.stringify(searchObj))
        dataConcat()
      }

      // 重置
      const handleReset = () => {
        searchObj.ssId = ''
        searchObj.plno = ''
        searchObj.speed = 60
        document.getElementById('fileInput').value = '';
        sectionLens.value = []
        reverseLens.value = []
        crossInfo.value = []
        greenData.forward = []
        greenData.reverse = []
      }

      // 处理模拟数据
      const apiPreFix = computed(() => {
        return isMock ? '' : apiUrl
      })

      // 处理模拟数据
      const api = computed(() => {
        return isMock ? mockApi : realApi
      })

      // 接口返回的全部子区信息
      const allSchemeList = ref([])

      // 获取子区的方案信息
      const getCrossScheme = () => {
        fetch(apiPreFix.value + api.value.getPlans)
          .then(res => res.json())
          .then(res => {
            allSchemeList.value = res

            const greenSearch = window.sessionStorage.getItem('GREEN_DATA')
            if (greenSearch) {
              const data = JSON.parse(greenSearch)
              searchObj.ssId = data.ssId ?? ''
              searchObj.plno = data.plno ?? ''
              searchObj.speed = data.speed ?? 60
              handleSearch()
            }
          })
      }

      const SUB_LIST = ref([])
      // const TIME_LIST = ref([])

      // 根据子区id获取当前的
      const currentSlaveList = computed(() => {
        const result = SUB_LIST.value.find(item => item.master === searchObj.ssId)
        return result.slave ?? []
      })

      const cancelConfirm = () => {
        alert.type = ''
        alert.text = ''
        alert.methods = ''
        alert.item = ''
      }

      // 确认操作
      const handleConfirm = async (alert) => {
        // console.log('methodsList[key]', methodsList[key]);
        try {
          await methodsList[alert.methods](alert.item)
          cancelConfirm()
        } catch {
          cancelConfirm()
        }
      }

      const handleLoginOut = () => {
        alert.text = `确定退出登录吗?`
        alert.type = MESSAGE_TYPE.CONFIRM
        alert.methods = 'goLogin'
      }

      const methodsList = {
        'goLogin': goLogin,
      }

      onMounted(() => {
        hasLogin(apiPreFix.value)
        getCrossScheme()
        // 页面变动时，canvas重新绘制
        window.addEventListener('resize', pageResize)
        const sessionData = JSON.parse(window.sessionStorage.getItem('BASE_DICTS'))
        if (!sessionData) {
          window.location.replace(window.location.origin)
          const currentPage = pageTab.find(item => item.key === 'static-green')
          currentPage && window.sessionStorage.setItem('CURRENT_PAGE', JSON.stringify(currentPage))
        }
        SUB_LIST.value = sessionData.areas
      })

      onUnmounted(() => {
        window.removeEventListener('resize', pageResize)
        // window.sessionStorage.removeItem('greenSchemes')
      })
      // -------------- 界面渲染逻辑 end ----------------
      return {
        baseMsgHeight,
        baseY,
        pointNumber,
        TIME_PORT_TYPE,
        sectionLens,
        reverseLens,
        crossInfo,
        greenData,
        allLength,
        getSvgWidth,
        getLinePosition,
        isResize,
        isEmpty,
        timePort,
        randomId,
        legend,
        safeSpace,
        searchObj,
        message,
        loading,
        SUB_LIST,
        SCHEME_CONFIG,
        pageTab,
        changeTab,
        alert,
        MESSAGE_TYPE,
        getTimeLinePosition,
        getVerticalLinePosition,
        xLinePosition,
        xTextPosition,
        getTimeItemXPosition,
        heightRate,
        getTimeItemYPosition,
        getTimeTextPosition,
        getGreenStartTextPosition,
        getGreenStartPosition,
        chartY,
        greenSvgHeight,
        xSvgHeight,
        getOptimizeLinePosition,
        handleSearch,
        handleReset,
        handleConfirm,
        cancelConfirm,
        handleLoginOut
      }
    },
  })
  app.mount("#green-ware");
</script>

</html>